package com.learning.mybatis.practise.page.plugin;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * ClassName: PagePlugin
 * Description:
 * Date: 2020/8/16 13:27 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class PagePlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        Object[] args = invocation.getArgs();
        Connection connection = (Connection) args[0];
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);

        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        // 判断查询
        if (sql.trim().toLowerCase().indexOf("select") != 0) {
            return invocation.proceed();
        }

        // 获取分页参数
        Object parameterObject = boundSql.getParameterObject();
        PageParam pageParam = null;
        // 支持 Map参数和@Param注解参数
        if (parameterObject instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> paramMap = (Map<String, Object>) parameterObject;
            for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
                Object value = entry.getValue();
                if (value instanceof PageParam) {
                    pageParam = (PageParam) value;
                    break;
                }
            }

        } else if (parameterObject instanceof PageParam) {
            pageParam = (PageParam) parameterObject;
        }
        // 没有分页参数或者设置不启用，不需要分页
        if (pageParam == null || !pageParam.isUseFlag()) {
            return invocation.proceed();
        }

        // 查询总数
        int total = 0;
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        Configuration configuration = mappedStatement.getConfiguration();
        String countSql = "select count(*) as total from (" + sql + ") $_page_total ";

        ResultSet resultSet = null;
        try (PreparedStatement preparedStatement = connection.prepareStatement(countSql)) {
            // 处理参数
            BoundSql countBS = new BoundSql(configuration, countSql,
                    boundSql.getParameterMappings(), boundSql.getParameterObject());
            ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, countBS);
            parameterHandler.setParameters(preparedStatement);

            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                total = resultSet.getInt("total");
            }
        } finally {
            if (resultSet != null) {
                resultSet.close();
            }
        }
        Integer pageSize = pageParam.getPageSize();
        int totalPage = total % pageSize == 0 ? total / pageSize : (total / pageSize) + 1;
        pageParam.setTotalPage(totalPage);
        pageParam.setTotal(total);

        // 分页查询
        Integer page = pageParam.getPage();
        String pageSql = "select * from (" + sql + ")  $_page_table limit " + (page - 1) * pageSize + ", " + pageSize + "";
        metaObject.setValue("delegate.boundSql.sql", pageSql);
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
